// ==UserScript==
// @name         b站视频导出markdown文件
// @namespace    https://bbs.tampermonkey.net.cn/
// @version      0.1.0
// @description  用于大模型RAG
// @author       口吃者
// @match        https://www.bilibili.com/video/*
// @require      https://scriptcat.org/lib/2691/1.0.0/sweetalert2.all.min-11.15.10.js
// @require      https://scriptcat.org/lib/2724/1.0.0/js%20%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%8E%8B%E7%BC%A9%E5%BA%93-LZString.js
// @grant        GM.setValue
// @grant        GM.getValue
// @grant        GM.deleteValue
// @grant        unsafeWindow
// @grant        GM_xmlhttpRequest
// @run-at       document-body
// @license MIT
// ==/UserScript==

(async function () {
    'use strict';
    class RequestMonitor {
        constructor() {
            // 预编译正则表达式提升匹配性能
            this._patterns = new Map(); // Map<RegExp, Processor>

            // 使用WeakMap防止内存泄漏
            this.pendingRequests = new WeakMap();

            // 缓存已验证的URL
            this._urlCache = {
                monitored: new Set(),
                ignored: new Set()
            };

            // 性能统计
            this.stats = {
                total: 0,
                processed: 0,
                errors: 0
            };

            this._initHooks();
        }
        /* pattern:正则表达式(只要满足其一就会进行监听处理)，processor：处理器函数(对满足的每一个pattern进行processor处理，一对多) */
        registerHandler(pattern, processor) {
            const regex = new RegExp(pattern);
            this._patterns.set(regex, processor);
            return this; // 支持链式调用
        }

        _initHooks() {
            // 保存原生方法引用
            const nativeFetch = unsafeWindow.fetch;
            const nativeXHROpen = XMLHttpRequest.prototype.open;
            const nativeXHRSend = XMLHttpRequest.prototype.send;

            const self = this;

            // Hook XHR open方法捕获URL
            XMLHttpRequest.prototype.open = function (method, url) {
                this._requestURL = url;
                return nativeXHROpen.apply(this, arguments);
            };

            // Hook XHR send
            XMLHttpRequest.prototype.send = function (body) {
                const xhr = this;
                const url = xhr._requestURL || '';

                // 前置检查
                if (!self._shouldMonitor(url)) {
                    return nativeXHRSend.apply(this, arguments);
                }

                const requestKey = {};
                self.pendingRequests.set(xhr, requestKey);
                self.stats.total++;

                // 添加事件监听
                xhr.addEventListener('load', () => self._handleXHRResponse(xhr, requestKey));
                xhr.addEventListener('error', () => self._cleanup(xhr));

                nativeXHRSend.apply(this, arguments);
            };

            // Hook Fetch
            unsafeWindow.fetch = async (input, init) => {
                const url = typeof input === 'string' ? input : input.url;

                if (!self._shouldMonitor(url)) {
                    return nativeFetch(input, init);
                }

                const requestKey = {};
                self.pendingRequests.set(requestKey, true);
                self.stats.total++;

                try {
                    const response = await nativeFetch(input, init);
                    self._handleFetchResponse(url, response.clone(), requestKey);
                    return response;
                } catch (error) {
                    self.stats.errors++;
                    self._cleanup(requestKey);
                    throw error;
                }
            };
        }

        _shouldMonitor(url) {
            if (this._urlCache.monitored.has(url)) return true;
            if (this._urlCache.ignored.has(url)) return false;

            for (const [regex] of this._patterns) {
                if (regex.test(url)) {
                    this._urlCache.monitored.add(url);
                    return true;
                }
            }

            this._urlCache.ignored.add(url);
            return false;
        }

        async _handleXHRResponse(xhr, requestKey) {
            try {
                const data = await this._parseResponse(xhr.response);
                await this._processData(xhr._requestURL, data);
                this.stats.processed++;
            } catch (error) {
                this.stats.errors++;
                console.error('XHR处理失败:', error);
            } finally {
                this._cleanup(xhr);
            }
        }

        async _handleFetchResponse(url, response, requestKey) {
            try {
                const data = await this._parseResponse(await response.text());
                await this._processData(url, data);
                this.stats.processed++;
            } catch (error) {
                this.stats.errors++;
                console.error('Fetch处理失败:', error);
            } finally {
                this._cleanup(requestKey);
            }
        }

        async _processData(url, rawData) {
            for (const [regex, processor] of this._patterns) {
                if (regex.test(url)) {
                    await processor(rawData);
                    break; // 仅匹配第一个处理器
                }
            }
        }

        async _parseResponse(data) {
            try {
                return JSON.parse(data);
            } catch {
                return data;
            }
        }

        _cleanup(target) {
            this.pendingRequests.delete(target);

            // 定期清理缓存防止内存增长
            if (this._urlCache.monitored.size > 1000) {
                this._urlCache.monitored.clear();
            }
        }

        // 调试方法
        printStats() {
            console.table({
                '总请求数': this.stats.total,
                '已处理请求': this.stats.processed,
                '当前挂起请求': this.pendingRequests.size,
                '错误数量': this.stats.errors,
                '缓存命中率': `${(this.stats.processed / this.stats.total * 100).toFixed(1)}%`
            });
        }
    }

    // storage-manager.js
    class StorageManager {
        constructor(options = {}) {
            // 配置参数
            this.namespace = options.namespace || 'defaultStorage';
            this.chunkSize = options.chunkSize || 500;      // 每个分片最大记录数
            this.maxChunks = options.maxChunks || 10;       // 最大保留分片数
            this.autoCleanThreshold = options.autoCleanThreshold || 5 * 1024 * 1024; // 5MB自动清理阈值
            this.compression = options.compression !== false; // 默认启用压缩 明确设置为 false 时，this.compression 才会被设置为 false

            // 状态跟踪
            this.currentChunk = 1;
            this.totalRecords = 0;
            this.lastCleanTime = 0;
        }

        // ======================== 核心接口 ========================

        // 静态工厂方法
        static async createInstance(options = {}) {
            const instance = new StorageManager(options);
            await instance.init();
            return instance;
        }

        /*  状态跟踪属性赋值 */
        async init() {
            await this._loadIndex();
            return this;
        }

        async save(data) {
            // 1. 获取当前分片
            let chunk = await this._getCurrentChunk();

            // 2. 检查分片容量
            if (chunk.length >= this.chunkSize) {
                // 更新状态跟踪属性
                await this._rotateChunk();
                chunk = [];
            }

            // 3. 添加数据
            chunk.push(data);
            this.totalRecords++;

            // 4. 保存分片
            await this._saveChunk(chunk);

            // 5. 自动清理检查
            // await this._autoCleanCheck();
        }

        async loadAll() {
            const chunks = [];
            for (let i = 1; i <= this.maxChunks; i++) {
                const chunk = await this._getChunk(i);
                if (chunk) chunks.push(...chunk);
            }
            // return this._deduplicate(chunks);
            return chunks;
        }

        async clear() {
            for (let i = 1; i <= this.maxChunks; i++) {
                await GM.deleteValue(this._chunkKey(i));
            }
            await this._saveIndex({ currentChunk: 1, totalRecords: 0 });
        }

        // ======================== 内部方法 ========================
        async _loadIndex() {
            const index = await GM.getValue(this._indexKey(), '{}');
            const { currentChunk = 1, totalRecords = 0 } = JSON.parse(index);
            this.currentChunk = currentChunk;
            this.totalRecords = totalRecords;
        }

        async _saveIndex() {
            const index = JSON.stringify({
                currentChunk: this.currentChunk,
                totalRecords: this.totalRecords,
                lastCleanTime: this.lastCleanTime
            });
            await GM.setValue(this._indexKey(), index);
        }

        async _getCurrentChunk() {
            return await this._getChunk(this.currentChunk) || [];
        }

        async _rotateChunk() {
            this.currentChunk = (this.currentChunk % this.maxChunks) + 1;
            await this._saveIndex();
        }

        async _saveChunk(chunk) {
            const data = this.compression
                ? LZString.compressToUTF16(JSON.stringify(chunk))
                : JSON.stringify(chunk);

            await GM.setValue(
                this._chunkKey(this.currentChunk),
                data
            );
        }

        async _getChunk(number) {
            const data = await GM.getValue(this._chunkKey(number));
            if (!data) return null;

            try {
                return JSON.parse(
                    this.compression
                        ? LZString.decompressFromUTF16(data)
                        : data
                );
            } catch (e) {
                console.error('分片数据解析失败:', e);
                return null;
            }
        }

        async _autoCleanCheck() {
            // 按时间清理（每10分钟）
            if (Date.now() - this.lastCleanTime > 600_000) {
                await this.cleanOldChunks();
                return;
            }

            // 按容量清理
            const size = await this._estimateStorageSize();
            if (size > this.autoCleanThreshold) {
                await this.cleanOldChunks();
                this.maxChunks = Math.max(3, Math.floor(this.maxChunks * 0.8));
            }
        }

        async cleanOldChunks() {
            const oldest = this.currentChunk > this.maxChunks
                ? this.currentChunk - this.maxChunks
                : 1;

            for (let i = 1; i <= oldest; i++) {
                await GM.deleteValue(this._chunkKey(i));
            }

            this.lastCleanTime = Date.now();
            await this._saveIndex();
        }

        async _estimateStorageSize() {
            let total = 0;
            for (let i = 1; i <= this.maxChunks; i++) {
                const data = await GM.getValue(this._chunkKey(i));
                total += data ? data.length : 0;
            }
            return total;
        }

        _deduplicate(data) {
            const seen = new Set();
            return data.filter(item => {
                const key = item.id || JSON.stringify(item);
                return seen.has(key) ? false : seen.add(key);
            });
        }

        // ======================== 工具方法 ========================
        _indexKey() {
            return `${this.namespace}_index`;
        }

        _chunkKey(number) {
            return `${this.namespace}_chunk_${number}`;
        }

        // ======================== 调试接口 ========================
        async printStats() {
            const size = await this._estimateStorageSize();
            console.log(`[StorageManager 状态报告]
       命名空间: ${this.namespace}
       当前分片: ${this.currentChunk}/${this.maxChunks}
       总记录数: ${this.totalRecords}
       预估存储: ${(size / 1024).toFixed(1)}KB
       最后清理: ${new Date(this.lastCleanTime).toLocaleTimeString()}
       压缩状态: ${this.compression ? '启用' : '关闭'}
      `);
        }
    }

    class BExportTool {
        constructor({videoTitle, videoAuthor, videoIntro, videoSubtitle, videoComments}) {
            this.videoTitle = videoTitle;
            this.videoAuthor = videoAuthor;
            this.videoIntro = videoIntro;
            this.videoSubtitle = videoSubtitle;
            this.videoComments = videoComments;
        }

        // 新增 Markdown 导出方法
        exportDsMarkdownData() { // 注意方法名驼峰式命名
            if (!this.markdownData) {
                console.error('No Markdown data to export');
                return;
            }

            // 创建标准 Markdown Blob（指定 MIME 类型）
            const blob = new Blob([this.markdownData], {
                type: 'text/markdown;charset=utf-8' // 或使用 text/plain
            });

            // 生成带时间戳的文件名（示例：chat_history_12345_20230815.md）
            const timestamp = new Date().toISOString().slice(0, 10).replace(/-/g, '');
            const filename = `Bilibili_${timestamp}.md`;

            // 创建并触发下载链接
            const url = URL.createObjectURL(blob);
            const anchor = document.createElement('a');
            anchor.href = url;
            anchor.download = filename;
            anchor.style.display = 'none';

            document.body.appendChild(anchor);
            anchor.click();

            // 清理资源
            document.body.removeChild(anchor);
            URL.revokeObjectURL(url);
        }

        toMarkdown() {
            // 处理标题和作者
            const titleSection = `## ${this.videoTitle}`;
            const authorSection = `作者 ${this.videoAuthor}`;

            // 处理视频简介
            const introSection = `### 视频简介\n${this.videoIntro}`;

            // 处理视频文案
            const subtitleSection = `### 视频文案\n${this.videoSubtitle}`;

            // 处理评论部分
            const commentItems = this.videoComments.map((comment, index) => {
                return `${index + 1}. 用户名:${comment.author}\n  点赞数${comment.likes}\n  评论内容:${comment.content}`;
            }).join('\n');

            const commentSection = `### 评论\n${commentItems}`;

            this.markdownData = [
                titleSection,
                authorSection,
                introSection,
                subtitleSection,
                commentSection
            ].join('\n');
            // 合并所有部分
            return this.markdownData;
        }
    }

    const storageComment = await StorageManager.createInstance({
        namespace: 'commentData',
        chunkSize: 1000,
        maxChunks: 20
    });
    const monitorTest = new RequestMonitor();
    var commentRpId = new Set();
    var videoDetails = {
        videoTitle: '',
        videoIntroduction: '',
        upName: ''
    };
    var fetchPageParams;
    //补环境参数
    var ct = "wbi_img_urls";
    var e = {
        "oid": "113402018078283",
        "type": 1,
        "mode": 3,
        "pagination_str": "{\"offset\":\"{\\\"type\\\":1,\\\"direction\\\":1,\\\"data\\\":{\\\"pn\\\":2}}\"}",
        "plat": 1,
        "web_location": 1315875
    };
    monitorTest.registerHandler('x/v2/reply/wbi/main', async data => {
        try {
            //是否第一次获取评论 判断StorageManager的totalRecords是否为0
            if (storageComment.totalRecords === 0) {
                let hasMatch = Array.from(monitorTest._urlCache.monitored).filter(element => element.includes('x/v2/reply/wbi/main'));
                fetchPageParams = getUrlComponent(hasMatch[0]);
                let pagination_str = {
                    offset: data.data.cursor.pagination_reply.next_offset
                };
                fetchPageParams["pagination_str"] = JSON.stringify(pagination_str);
                e["pagination_str"] = JSON.stringify(pagination_str);
                e["oid"] = fetchPageParams["oid"];
                e["web_location"] = fetchPageParams['web_location'];
                var ltFucRes = lt(e);
                fetchPageParams["w_rid"] = ltFucRes["w_rid"];
                fetchPageParams["wts"] = ltFucRes["wts"];
            }
            const replies = data.data.replies;
            for (let i = 0; i < replies.length; i++) {
                const reply = replies[i];
                const rpId = reply.rpid;
                const rpContent = reply.content.message;
                const rpName = reply.member.uname;
                const rpLike = reply.like;

                if (commentRpId.has(rpId)) {
                    continue;
                }
                await storageComment.save({ "author": rpName, "content": rpContent, "likes": rpLike });
                commentRpId.add(rpId);
            }
        } catch (e) {
            console.log(e);
        }
    });
    monitorTest.registerHandler('x/player/wbi/v2', async data => {
        try {
            await GM.deleteValue('subTitleUrl');
            await GM.setValue('subTitleUrl', data.data.subtitle.subtitles[0].subtitle_url);
        } catch (e) {
            console.log(e);
        }
    });
    window.addEventListener('load', addPanel);
    window.addEventListener('load', async () => {
        // 重置所有值
        await storageComment.clear();
        videoDetails.videoIntroduction = document.querySelector('#v_desc > div > span')?.textContent;
        videoDetails.upName = removeSpacesAndNewlines(document.querySelector('div.up-detail-top > a.up-name')?.textContent);
        videoDetails.videoTitle = document.querySelector('#viewbox_report > div.video-info-title > div > h1')?.textContent;
    });
    function addPanel() {
        function genButton(text, foo, id, fooParams = {}) {
            let b = document.createElement('button');
            b.textContent = text;
            b.style.verticalAlign = 'inherit';
            // 使用箭头函数创建闭包来保存 fooParams 并传递给 foo
            b.addEventListener('click', () => {
                foo.call(b, ...Object.values(fooParams)); // 使用 call 方法确保 this 指向按钮对象
            });
            if (id) { b.id = id };
            return b;
        }

        function changeRangeDynamics() {
            const value = parseInt(this.value, 10);
            // 只能通过 DOM 方法改变
            document.querySelector('#swal-range > output').textContent = value;
        }

        async function openPanelFunc() {
            var swalRangeValue = 20;
            const { value: formValues } = await Swal.fire({
                title: "<strong>Select Number</strong>",
                showCancelButton: true,
                draggable: true,
                cancelButtonText: 'cancel',
                confirmButtonText: 'export',
                confirmButtonColor: "#FFD700",
                cancelButtonColor: "#8FBC8F",
                //class="swal2-range" swalalert框架可能会对其有特殊处理，导致其内标签的id声明失效
                html: `
                  <ul>
                    <li>saved comments:<i> - </i></li>
                    <li>subtitle present:<i> yes </i></li>
                  </ul>
                  <div class="swal2-range" id="swal-range" style="display: flex;">
                    <input type="range" min="0" max="300" step="20" value="20">
                    <output>${swalRangeValue}</output>
                  </div>
                `,
                focusConfirm: false,
                didOpen: async () => {
                    const swalRange = document.querySelector('#swal-range input');
                    const commentsNumber = document.querySelector('#swal2-html-container>ul>li:nth-child(1)>i');
                    const ifSubtitle = document.querySelector('#swal2-html-container>ul>li:nth-child(2)>i');
                    const subtitleUrl = await GM.getValue('subTitleUrl', '');
                    commentsNumber.textContent = storageComment.totalRecords;
                    ifSubtitle.textContent = subtitleUrl ? 'yes' : 'no';
                    swalRange.addEventListener('input', changeRangeDynamics);
                },
                willClose: () => {
                    // 在关闭前清除事件监听器以防止内存泄漏
                    const swalRange = document.querySelector('#swal-range input');
                    swalRange.removeEventListener('input', changeRangeDynamics);
                },
                preConfirm: () => {
                    swalRangeValue = document.querySelector('#swal-range > output').textContent;
                    return [
                        swalRangeValue
                    ];
                }
            });
            if (formValues) {
                getAllCommentsAndExport(formValues[0]);
            }
        }

        let myButton = genButton('BExport', openPanelFunc, 'BExport');
        document.body.appendChild(myButton);

        var css_text = `
            #BExport {
                position: fixed;
                color: rgb(211, 67, 235);
                top: 30%;
                left: -20px;/* 初始状态下左半部分隐藏 */
                transform: translateY(-50%);
                z-index: 1000; /* 确保按钮在最前面 */
                padding: 10px 24px;
                border-radius: 5px;
                cursor: pointer;
                border: 0;
                background-color: white;
                box-shadow: rgb(0 0 0 / 5%) 0 0 8px;
                letter-spacing: 1.5px;
                text-transform: uppercase;
                font-size: 9px;
                transition: all 0.5s ease;
            }
            #BExport:hover {
                left: 0%; /* 鼠标悬停时完整显示 */
                letter-spacing: 3px;
                background-image: linear-gradient(to top, #fad0c4 0%, #fad0c4 1%, #ffd1ff 100%);
                box-shadow: rgba(211, 67, 235, 0.7) 0px 7px 29px 0px; /* 更柔和的紫色阴影，带透明度 */
            }
            
            #BExport:active {
                letter-spacing: 3px;
                background-image: linear-gradient(to top, #fad0c4 0%, #fad0c4 1%, #ffd1ff 100%);
                box-shadow: rgba(211, 67, 235, 0.5) 0px 0px 0px 0px; /* 活动状态下的阴影，保持一致性 */
                transition: 100ms;
            }
        `
        GMaddStyle(css_text);
    }

    function GMaddStyle(css) {
        var myStyle = document.createElement('style');
        myStyle.textContent = css;
        var doc = document.head || document.documentElement;
        doc.appendChild(myStyle);
    }

    function removeSpacesAndNewlines(str) {
        // 使用正则表达式匹配所有空格和换行符，并替换为空字符串
        return str.replace(/[\s\r\n]+/g, '');
    }

    async function fetchNextPageData(comment_params) {
        const finalUrl = getFinalCommentUrl(comment_params);
        const response = await fetch(finalUrl, {
            headers: {
                'origin': 'https://www.bilibili.com'
            },
            credentials: 'include'  // 明确指定携带cookies
        });
        return await response.json();
    }

    async function fetchSubtitle() {
        const subtitleUrl = await GM.getValue('subTitleUrl', '');
        if (subtitleUrl) {
            const response = await fetch('https://' + subtitleUrl, {
                headers: {
                    'origin': 'https://www.bilibili.com'
                },
                credentials: 'include'  // 明确指定携带cookies
            });
            return response.json();
        } else {
            return '';
        }
    }

    function sendGetRequestWithGM(url) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                headers: {
                    'Content-Type': 'application/json'
                },
                onload: function (response) {
                    resolve(JSON.parse(response.responseText));
                },
                onerror: function (error) {
                    reject(error);
                }
            });
        });
    }

    function getFinalCommentUrl(params) {
        // 指定参数的顺序
        const orderKeys = ["oid", "type", "mode", "plat", "web_location", "pagination_str", "w_rid", "wts"];

        // 按照指定顺序构建参数列表
        const orderedParams = orderKeys
            .filter(key => params.hasOwnProperty(key))
            .map(key => key === 'pagination_str'
                ? `${key}=${encodeURIComponent(params[key])}`
                : `${key}=${params[key]}`);

        // 构建新的URL
        const newUrl = 'https://api.bilibili.com/x/v2/reply/wbi/main?' + orderedParams.join('&');

        return newUrl;
    };

    function getUrlComponent(targetUrl) {
        // 提取查询字符串部分
        const queryString = targetUrl.split('?')[1];
        // 定义正则表达式来匹配参数
        const regex = /([^&=]+)=([^&]*)/g;
        // 创建一个对象来存储参数
        const params = {};
        let match;
        while ((match = regex.exec(queryString)) !== null) {
            const key = decodeURIComponent(match[1]);
            const value = decodeURIComponent(match[2]);
            params[key] = value;
        }
        return params;
    };

    function extractContentsToString(dataArray) {
        return dataArray
            .map(item => item.content || '') // 安全提取content，防止undefined
            .join(';'); // 默认用空字符串连接，可按需改为换行符\n或其他分隔符
    }

    async function getAllCommentsAndExport(targetAmount) {
        try {
            while (storageComment.totalRecords < targetAmount) {
                const commentData = await fetchNextPageData(fetchPageParams);
                const replies = commentData.data.replies;
                if (!replies || replies.length === 0) {
                    break;
                }
                for (let i = 0; i < replies.length; i++) {
                    const reply = replies[i];
                    const rpId = reply.rpid;
                    const rpContent = reply.content.message;
                    const rpName = reply.member.uname;
                    const rpLike = reply.like;
    
                    if (commentRpId.has(rpId)) {
                        continue;
                    }
                    await storageComment.save({ "author": rpName, "content": rpContent, "likes": rpLike });
                    commentRpId.add(rpId);
                }
            }
            const subtitleUrl = await GM.getValue('subTitleUrl', '');
            let subtitleData;
            if (subtitleUrl) {
                let subtitleDataJson = await sendGetRequestWithGM('https://' + subtitleUrl);
                subtitleData = extractContentsToString(subtitleDataJson.body);
            }
            const videoComments = await storageComment.loadAll();
            const bExportTool = new BExportTool({
                "videoTitle": videoDetails.videoTitle,
                "videoAuthor": videoDetails.upName,
                "videoIntro": videoDetails.videoIntroduction,
                "videoSubtitle": subtitleData,
                "videoComments": videoComments
            });
            bExportTool.toMarkdown();
            bExportTool.exportDsMarkdownData();
        } catch (error) {
            console.error('Error:', error);
        }
    }

    /* 补环境 */
    const t_stringToBytes = function (e) {
        return n_stringToBytes(unescape(encodeURIComponent(e)))
    };
    const n_stringToBytes = function (e) {
        for (var t = [], r = 0; r < e.length; r++)
            t.push(255 & e.charCodeAt(r));
        return t
    };
    const e_bytesToWords = function (e) {
        for (var t = [], r = 0, n = 0; r < e.length; r++,
            n += 8)
            t[n >>> 5] |= e[r] << 24 - n % 32;
        return t
    };
    const o_ff = function (e, t, r, n, o, i, a) {
        var u = e + (t & r | ~t & n) + (o >>> 0) + a;
        return (u << i | u >>> 32 - i) + t
    };
    const o_gg = function (e, t, r, n, o, i, a) {
        var u = e + (t & n | r & ~n) + (o >>> 0) + a;
        return (u << i | u >>> 32 - i) + t
    };
    const o_hh = function (e, t, r, n, o, i, a) {
        var u = e + (t ^ r ^ n) + (o >>> 0) + a;
        return (u << i | u >>> 32 - i) + t
    };
    const o_ii = function (e, t, r, n, o, i, a) {
        var u = e + (r ^ (t | ~n)) + (o >>> 0) + a;
        return (u << i | u >>> 32 - i) + t
    };
    const t_rotl = function (e, t) {
        return e << t | e >>> 32 - t
    };
    const e_endian = function (e) {
        if (e.constructor == Number)
            return 16711935 & t_rotl(e, 8) | 4278255360 & t_rotl(e, 24);
        for (var r = 0; r < e.length; r++)
            e[r] = e_endian(e[r]);
        return e
    };
    const o = function o(i, a) {
        i.constructor == String ? i = a && "binary" === a.encoding ? n_stringToBytes(i) : t_stringToBytes(i) : r(i) ? i = Array.prototype.slice.call(i, 0) : Array.isArray(i) || i.constructor === Uint8Array || (i = i.toString());
        for (var u = e_bytesToWords(i), s = 8 * i.length, c = 1732584193, l = -271733879, f = -1732584194, d = 271733878, p = 0; p < u.length; p++)
            u[p] = 16711935 & (u[p] << 8 | u[p] >>> 24) | 4278255360 & (u[p] << 24 | u[p] >>> 8);
        u[s >>> 5] |= 128 << s % 32,
            u[14 + (s + 64 >>> 9 << 4)] = s;
        var h = o_ff
            , y = o_gg
            , v = o_hh
            , b = o_ii;
        for (p = 0; p < u.length; p += 16) {
            var m = c
                , w = l
                , g = f
                , x = d;
            c = h(c, l, f, d, u[p + 0], 7, -680876936),
                d = h(d, c, l, f, u[p + 1], 12, -389564586),
                f = h(f, d, c, l, u[p + 2], 17, 606105819),
                l = h(l, f, d, c, u[p + 3], 22, -1044525330),
                c = h(c, l, f, d, u[p + 4], 7, -176418897),
                d = h(d, c, l, f, u[p + 5], 12, 1200080426),
                f = h(f, d, c, l, u[p + 6], 17, -1473231341),
                l = h(l, f, d, c, u[p + 7], 22, -45705983),
                c = h(c, l, f, d, u[p + 8], 7, 1770035416),
                d = h(d, c, l, f, u[p + 9], 12, -1958414417),
                f = h(f, d, c, l, u[p + 10], 17, -42063),
                l = h(l, f, d, c, u[p + 11], 22, -1990404162),
                c = h(c, l, f, d, u[p + 12], 7, 1804603682),
                d = h(d, c, l, f, u[p + 13], 12, -40341101),
                f = h(f, d, c, l, u[p + 14], 17, -1502002290),
                c = y(c, l = h(l, f, d, c, u[p + 15], 22, 1236535329), f, d, u[p + 1], 5, -165796510),
                d = y(d, c, l, f, u[p + 6], 9, -1069501632),
                f = y(f, d, c, l, u[p + 11], 14, 643717713),
                l = y(l, f, d, c, u[p + 0], 20, -373897302),
                c = y(c, l, f, d, u[p + 5], 5, -701558691),
                d = y(d, c, l, f, u[p + 10], 9, 38016083),
                f = y(f, d, c, l, u[p + 15], 14, -660478335),
                l = y(l, f, d, c, u[p + 4], 20, -405537848),
                c = y(c, l, f, d, u[p + 9], 5, 568446438),
                d = y(d, c, l, f, u[p + 14], 9, -1019803690),
                f = y(f, d, c, l, u[p + 3], 14, -187363961),
                l = y(l, f, d, c, u[p + 8], 20, 1163531501),
                c = y(c, l, f, d, u[p + 13], 5, -1444681467),
                d = y(d, c, l, f, u[p + 2], 9, -51403784),
                f = y(f, d, c, l, u[p + 7], 14, 1735328473),
                c = v(c, l = y(l, f, d, c, u[p + 12], 20, -1926607734), f, d, u[p + 5], 4, -378558),
                d = v(d, c, l, f, u[p + 8], 11, -2022574463),
                f = v(f, d, c, l, u[p + 11], 16, 1839030562),
                l = v(l, f, d, c, u[p + 14], 23, -35309556),
                c = v(c, l, f, d, u[p + 1], 4, -1530992060),
                d = v(d, c, l, f, u[p + 4], 11, 1272893353),
                f = v(f, d, c, l, u[p + 7], 16, -155497632),
                l = v(l, f, d, c, u[p + 10], 23, -1094730640),
                c = v(c, l, f, d, u[p + 13], 4, 681279174),
                d = v(d, c, l, f, u[p + 0], 11, -358537222),
                f = v(f, d, c, l, u[p + 3], 16, -722521979),
                l = v(l, f, d, c, u[p + 6], 23, 76029189),
                c = v(c, l, f, d, u[p + 9], 4, -640364487),
                d = v(d, c, l, f, u[p + 12], 11, -421815835),
                f = v(f, d, c, l, u[p + 15], 16, 530742520),
                c = b(c, l = v(l, f, d, c, u[p + 2], 23, -995338651), f, d, u[p + 0], 6, -198630844),
                d = b(d, c, l, f, u[p + 7], 10, 1126891415),
                f = b(f, d, c, l, u[p + 14], 15, -1416354905),
                l = b(l, f, d, c, u[p + 5], 21, -57434055),
                c = b(c, l, f, d, u[p + 12], 6, 1700485571),
                d = b(d, c, l, f, u[p + 3], 10, -1894986606),
                f = b(f, d, c, l, u[p + 10], 15, -1051523),
                l = b(l, f, d, c, u[p + 1], 21, -2054922799),
                c = b(c, l, f, d, u[p + 8], 6, 1873313359),
                d = b(d, c, l, f, u[p + 15], 10, -30611744),
                f = b(f, d, c, l, u[p + 6], 15, -1560198380),
                l = b(l, f, d, c, u[p + 13], 21, 1309151649),
                c = b(c, l, f, d, u[p + 4], 6, -145523070),
                d = b(d, c, l, f, u[p + 11], 10, -1120210379),
                f = b(f, d, c, l, u[p + 2], 15, 718787259),
                l = b(l, f, d, c, u[p + 9], 21, -343485551),
                c = c + m >>> 0,
                l = l + w >>> 0,
                f = f + g >>> 0,
                d = d + x >>> 0
        }
        return e_endian([c, l, f, d])
    };
    function ft(e) {
        return e.substring(e.lastIndexOf("/") + 1, e.length).split(".")[0]
    };
    const e_wordsToBytes = function (e) {
        for (var t = [], r = 0; r < 32 * e.length; r += 8)
            t.push(e[r >>> 5] >>> 24 - r % 32 & 255);
        return t
    };
    const e_bytesToHex = function (e) {
        for (var t = [], r = 0; r < e.length; r++)
            t.push((e[r] >>> 4).toString(16)),
                t.push((15 & e[r]).toString(16));
        return t.join("")
    };
    const at = function (t, r) {
        if (null == t)
            throw new Error("Illegal argument " + t);
        var i = e_wordsToBytes(o(t, r));
        return r && r.asBytes ? i : r && r.asString ? n.bytesToString(i) : e_bytesToHex(i)
    };
    function lt(e) {
        var t, r, n = function (e) {
            var t;
            if (e.useAssignKey)
                return {
                    imgKey: e.wbiImgKey,
                    subKey: e.wbiSubKey
                };
            var r = (null === (t = function (e) {
                try {
                    return localStorage.getItem(e)
                } catch (e) {
                    return null
                }
            }(ct)) || void 0 === t ? void 0 : t.split("-")) || []
                , n = r[0]
                , o = r[1]
                , i = n ? ft(n) : e.wbiImgKey
                , a = o ? ft(o) : e.wbiSubKey;
            return {
                imgKey: i,
                subKey: a
            }
        }(arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {
            wbiImgKey: "",
            wbiSubKey: ""
        }), o = n.imgKey, i = n.subKey;
        if (o && i) {
            for (var a = (t = o + i,
                r = [],
                [46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40, 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11, 36, 20, 34, 44, 52].forEach((function (e) {
                    t.charAt(e) && r.push(t.charAt(e))
                }
                )),
                r.join("").slice(0, 32)), u = Math.round(Date.now() / 1e3), s = Object.assign({}, e, {
                    wts: u
                }), c = Object.keys(s).sort(), l = [], f = /[!'()*]/g, d = 0; d < c.length; d++) {
                var p = c[d]
                    , h = s[p];
                h && "string" == typeof h && (h = h.replace(f, "")),
                    null != h && l.push("".concat(encodeURIComponent(p), "=").concat(encodeURIComponent(h)))
            }
            var y = l.join("&");
            return {
                w_rid: at(y + a),
                wts: u.toString()
            }
        }
        return null
    };
})();